/*
 * Copyright 2019 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.example.fep.netty.client;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.example.fep.netty.common.FepProtocolDecoder;
import io.netty.example.fep.netty.common.FepProtocolEncoder;
import io.netty.example.fep.netty.common.FepProtocolMsg;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * Description：
 * netty 客户端
 *
 * @author : chenjie03@imdada.cn
 * Date: 2020/5/26
 * Time: 13:50
 */
public final class NettyClient {

    private static final Logger logger = LoggerFactory.getLogger(NettyClient.class);

    private String clientId;
    private String host;
    private int port;

    private Bootstrap bootstrap;
    private Channel channel;


    /**
     * 获取 clientId
     *
     * @return clientId
     */
    public String getClientId() {
        return clientId;
    }


    /**
     * 获取 host
     *
     * @return host
     */
    public String getHost() {
        return host;
    }

    /**
     * 获取 port
     *
     * @return port
     */
    public int getPort() {
        return port;
    }

    public NettyClient(String clientId, String host, int port, ClientHandler handler) {
        this.clientId = clientId;
        this.host = host;
        this.port = port;
        init(handler);
        connect();
    }

    /**
     * 初始化客户端
     */
    private void init(ClientHandler handler) {
        bootstrap = new Bootstrap();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final NettyClient client = this;
        final ClientHandler myHandler = handler;
        bootstrap.group(workerGroup);
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel ch) {
                ch.pipeline().addLast("encoder", new FepProtocolEncoder());
                ch.pipeline().addLast("decoder", new FepProtocolDecoder());
                ch.pipeline().addLast("heartBeatHandler", new IdleStateHandler(0, 20, 0, TimeUnit.SECONDS));
                ch.pipeline().addLast("handler", new ClientMainHandler(client, myHandler));
            }
        });
        logger.info("client 初始化完成");
    }

    /**
     * 与服务端建立连接
     * （注册重连机制）
     */
    public void connect() {
        ChannelFuture future = bootstrap.connect(host, port);
        if (channel != null && channel.isActive()) {
            return;
        }
        //添加监听器，失连自动重连
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture futureListener) {
                if (futureListener.isSuccess()) {
                    channel = futureListener.channel();
                    logger.info("client 连接 server [{}:{}] 成功", host, port);
                } else {
                    logger.info("client 连接 server [{}:{}] 失败，将在 10s 后尝试重新建立连接", host, port);
                    futureListener.channel().eventLoop().schedule(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                connect();
                            } catch (Exception e) {
                                logger.error("client 重接 server [" + host + ":" + port + "] 失败：e=", e);
                            }
                        }
                    }, 10L, TimeUnit.SECONDS);
                }
            }
        });
        this.channel = future.channel();
    }


    /**
     * 客户端发送自定义消息
     *
     * @param msg 消息体
     * @throws InterruptedException 中断异常
     */
    public void send(String msg) throws InterruptedException {
        logger.info("client => server [{}:{}] msg:{}", host, port, msg);
        FepProtocolMsg protocol = new FepProtocolMsg(FepProtocolMsg.Constants.TYPE_OTHER, msg);
        channel.writeAndFlush(protocol).sync();
    }

    /**
     * 客户端发送自定义消息
     *
     * @param type 消息类型
     * @param msg  消息体
     * @throws InterruptedException 中断异常
     * @see FepProtocolMsg.Constants 消息类型
     */
    public void send(byte type, String msg) throws InterruptedException {
        logger.info("client => server [{}:{}] type:{}, msg:{}", host, port, type, msg);
        FepProtocolMsg protocol = new FepProtocolMsg(type, msg);
        channel.writeAndFlush(protocol).sync();
    }

}
